package SocialNet;

import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.index.Index;
import org.neo4j.helpers.collection.IterableWrapper;
import static SocialNet.RelTypes.A_PERSON;
import static SocialNet.RelTypes.REF_PERSONS;
import java.util.ArrayList;

public class Framework {
    private final GraphDatabaseService graphDb;
    private final Index<Node> index;
    private final Node personRefNode;
    public ArrayList<Person> persons;

    public Framework(GraphDatabaseService graphDb, Index<Node> index)
    {
        this.graphDb = graphDb;
        this.index = index;
        personRefNode = getPersonsRootNode( graphDb );
        persons = new ArrayList<Person>();
    }

    private Node getPersonsRootNode( GraphDatabaseService graphDb )
    {
        Relationship rel = graphDb.getReferenceNode().getSingleRelationship(
                REF_PERSONS, Direction.OUTGOING );
        if ( rel != null )
            return rel.getEndNode();
        else {
            Transaction tx = this.graphDb.beginTx();
            try {
                Node refNode = this.graphDb.createNode();
                this.graphDb.getReferenceNode().createRelationshipTo( refNode,
                        REF_PERSONS );
                tx.success();
                System.out.println("ref...");
                return refNode;
            }
            finally {
                tx.finish();
            }
        }
    }

    public Person createPerson( String name, String gender ) {
        // to guard against duplications we use the lock grabbed on ref node
        // when
        // creating a relationship and are optimistic about person not existing
        Transaction tx = graphDb.beginTx();
        try
        {
            Node newPersonNode = graphDb.createNode();
            personRefNode.createRelationshipTo(newPersonNode, A_PERSON);
            System.out.print("create person...");
            // lock now taken, we can check if already exist in index
            Node alreadyExist = index.get(Person.USERNAME, name).getSingle();
            if ( alreadyExist != null ) {
                tx.failure();
                //throw new Exception( "Person with this name already exists " );
            }
            newPersonNode.setProperty( Person.USERNAME, name);
            newPersonNode.setProperty( Person.GENDER, gender);
            index.add(newPersonNode, Person.USERNAME, name);
            persons.add(new Person(newPersonNode));
            tx.success();
            System.out.println("done");
            return new Person(newPersonNode);
        }
        finally {
            tx.finish();
        }
    }

    public Person getPersonByName(String name){
        Node personNode = index.get( Person.USERNAME, name ).getSingle();
        if ( personNode == null ) {
            throw new IllegalArgumentException( "Person[" + name + "] not found" );
        }
        return new Person( personNode );
    }
       
    public Person getPersonByIndex(int index) {
        return persons.get(index);   	
        
    }
    
/*    public int getPersonNodeId(String name) {
    	//return index.get( Person.USERNAME, name ).getId();
    }*/

    public void deletePerson( Person person )
    {
        Transaction tx = graphDb.beginTx();
        try
        {
            Node personNode = person.getNode();
            index.remove( personNode, Person.USERNAME, person.getName() );
            for ( Person friend : person.getFriends() )
            {
                person.removeFriend( friend );
            }
            personNode.getSingleRelationship( A_PERSON, Direction.INCOMING ).delete();

            for ( Status status : person.getStatus() )
            {
                Node statusNode = status.getNode();
                for ( Relationship r : statusNode.getRelationships() ) {
                    r.delete();
                }
                statusNode.delete();
            }
            personNode.delete();
            persons.remove(person);
            tx.success();
        }
        finally {
            tx.finish();
        }
    }

    public Iterable<Person> getAllPersons()
    {
        return new IterableWrapper<Person, Relationship>(
                personRefNode.getRelationships( A_PERSON ) ) {
            @Override
            protected Person underlyingObjectToObject(Relationship personRel) {
                return new Person(personRel.getEndNode());
            }
        };
    }
}

